import { ImageLayout, Pixel } from "../model/v3pro.image.layout";
import { ImageLayoutList } from "../util/v3pro.layout.list.util";
import {
  OutPortLayout,
  PanelLayout,
  ScanBoardLayout,
  SendBoxLayout,
} from "../model/v3pro.image.layout.extend";
import { V3ProBinaryPixel } from "../util/v3pro.binary.pixel";
import { V3proScreenImageHelper } from "./v3pro.screen.image.helper";
import {
  V3proScreenAllStatus,
  V3proSendBoxOutPort,
  V3proSendBoxScanBoard,
} from "../../../api/v3pro-api/v3pro.api.response.model";
import { PanelGroupKey, ScanBoardKey, SendboxKey } from "../model/v3pro.model";

/**
 * 搜索结果
 */
export class ScreenErrLayouts {
  constructor(public screen: ImageLayout) {}

  sendBoxes: SendBoxLayout[] = [];
  outPorts: OutPortLayout[] = [];
  scanBoards: ScanBoardLayout[] = [];
  panels: PanelLayout[] = [];
  pixels: Pixel[] = [];
}

/**
 * 屏幕错误信息帮助类
 * 1. 用于计算屏幕错误的layout信息
 * 2. 给 {@link V3proScreenImageHelper} 提供分析支持
 */
export class V3proScreenLayoutHelper {
  constructor(private __allStatus: V3proScreenAllStatus) {
    this.__needAcc = !this.__allStatus.isAbsolute;
    this.screenLayout = new ImageLayout(this.__allStatus.layout);
    this.__loadAllScanBoards();
    this.__loadLayout();
  }

  private readonly __needAcc: boolean;
  readonly screenLayout: ImageLayout;
  readonly scanBoardInfos = new Map<string, V3proSendBoxScanBoard>();

  readonly sendBoxLayouts = new ImageLayoutList<SendBoxLayout>("id");
  readonly outPortLayouts = new ImageLayoutList<OutPortLayout>();
  readonly scanBoardLayouts = new ImageLayoutList<ScanBoardLayout>("id");
  readonly panelLayouts = new ImageLayoutList<PanelLayout>();

  exportLayout() {
    return this.sendBoxLayouts.map((box) => box.toLayout());
  }

  /**
   * 克隆自己
   * 生成一个新的布局
   */
  clone() {
    return new V3proScreenLayoutHelper(this.__allStatus);
  }

  /**
   * 根据像素点查找发送盒
   * @param pixels
   */
  findSendBoxByPixels(pixels: Pixel[]) {
    return this.sendBoxLayouts.filterByPixels(pixels);
  }

  /**
   * 根据像素点查找输出口
   * @param pixels
   */
  findOutPortByPixels(pixels: Pixel[]) {
    const sendBoxes = this.findSendBoxByPixels(pixels);
    return sendBoxes.map((sb) => sb.outPorts.filterByPixels(pixels)).flat();
  }

  /**
   * 根据像素点查找扫描板
   * @param pixels
   */
  findScanBoardByPixels(pixels: Pixel[]) {
    const outPorts = this.findOutPortByPixels(pixels);
    return outPorts.map((op) => op.scanBoards.filterByPixels(pixels)).flat();
  }

  /**
   * 根据像素点查找面板
   * @param pixels
   */
  findPanelByPixels(pixels: Pixel[]) {
    const scanBoards = this.findScanBoardByPixels(pixels);
    return scanBoards.map((sb) => sb.panels.filterByPixels(pixels)).flat();
  }

  /**
   * 根据像素点查找面板
   * @param pixel
   */
  findPanelByPixel(pixel: Pixel) {
    const sendBox = this.sendBoxLayouts.getByPixel(pixel);
    if (!sendBox) return undefined;
    const outPort = sendBox.outPorts.getByPixel(pixel);
    if (!outPort) return undefined;
    const scanBoard = outPort.scanBoards.getByPixel(pixel);
    if (!scanBoard) return undefined;
    return scanBoard.panels.getByPixel(pixel);
  }

  /**
   * 设置发送盒错误
   * @param id
   * @param childLevel
   */
  setSendBoxErrById(id: string, childLevel: number = 100) {
    const sendBox = this.sendBoxLayouts.getByKey(id);
    if (!sendBox) return;
    sendBox.setError();
    if (childLevel > 0) {
      sendBox.outPorts.forEach((outPort) =>
        this.setOutPortErrByIp(sendBox.id, outPort.ip, childLevel - 1, 0),
      );
    }
  }

  /**
   * 设置发送盒错误
   * @param keyParams
   * @param childLevel
   */
  setSendBoxErrBySendBoxKeys(keyParams: SendboxKey[], childLevel: number = 100) {
    keyParams.forEach((key) => {
      this.setSendBoxErrById(key.id, childLevel);
    });
  }

  /**
   * 设置输出口错误
   * @param sendBoxId
   * @param ip
   * @param childLevel
   * @param parentLevel
   */
  setOutPortErrByIp(
    sendBoxId: string,
    ip: string,
    childLevel: number = 100,
    parentLevel: number = 100,
  ) {
    const sendBox = this.sendBoxLayouts.getByKey(sendBoxId);
    const outPort = sendBox?.outPorts.getByKey(ip);
    outPort?.setError();
    if (parentLevel > 0) {
      this.setSendBoxErrById(sendBoxId, 0);
      outPort?.belongSendBox.setError();
    }
    if (childLevel > 0) {
      outPort?.scanBoards.forEach((scanBoard) =>
        this.setScanBoardErrById(scanBoard.id, childLevel - 1, 0),
      );
    }
  }

  /**
   * 设置扫描板错误
   * @param id
   * @param childLevel
   * @param parentLevel
   */
  setScanBoardErrById(id: string, childLevel: number = 100, parentLevel: number = 100) {
    const scanBoard = this.scanBoardLayouts.getByKey(id);
    if (!scanBoard) return;
    scanBoard.setError();
    if (parentLevel > 0) {
      this.setOutPortErrByIp(
        scanBoard.belongSendBox.id,
        scanBoard.belongOutPort.ip,
        0,
        parentLevel - 1,
      );
    }
    if (childLevel > 0) {
      scanBoard.panels.forEach((panel) => panel.setError());
    }
  }

  /**
   * 设置扫描板错误
   * @param keyParams
   * @param childLevel
   * @param parentLevel
   */
  setScanBoardErrByScanBoardKeys(
    keyParams: ScanBoardKey[],
    childLevel: number = 100,
    parentLevel: number = 100,
  ) {
    keyParams.forEach((k) => {
      this.setScanBoardErrById(k.id, childLevel, parentLevel);
    });
  }

  /**
   * 设置面板错误
   * @param scanBoardId
   * @param index
   * @param parentLevel
   */
  setPanelErrById(scanBoardId: string, index: number, parentLevel: number = 100) {
    const scanBoard = this.scanBoardLayouts.getByKey(scanBoardId);
    const panel = scanBoard?.panels.getByKey(index);
    if (!panel) return;
    panel.setError();
    if (parentLevel > 0) {
      this.setScanBoardErrById(panel.belongScanBoard.id, 0, parentLevel - 1);
    }
  }

  /**
   * 设置面板错误
   * @param group
   * @param parentLevel
   */
  setPanelErrByPanelGroups(group: PanelGroupKey[], parentLevel: number = 100) {
    group.forEach((g) => {
      g.panels.forEach((p) => {
        this.setPanelErrById(g.id, p.index, parentLevel);
      });
    });
  }

  /**
   * 设置像素点错误
   * @param pixels
   * @param parentLevel
   */
  setPixelErrByBinary(pixels: V3ProBinaryPixel, parentLevel: number = 1) {
    pixels.forEachTrue((x, y) => {
      const panel = this.findPanelByPixel({ x, y });
      if (!panel) {
        return;
      }
      panel.setErrPixel({ x, y });
    });
    const setErr = (panel: PanelLayout) => {
      if (!panel.isDeadPixelErr()) return;
      panel.setError();
      if (parentLevel > 0) {
        this.setScanBoardErrById(panel.belongScanBoard.id, 0, parentLevel - 1);
      }
    };
    if (parentLevel-- > 0) this.panelLayouts.forEach(setErr);
  }

  /**
   * 加载所有扫描板，方便后面直接从这个map中获取扫描版详情
   * @private
   */
  private __loadAllScanBoards() {
    this.__allStatus.sendBoxs.forEach((sb) => {
      sb.scanBoards.forEach((sc) => {
        this.scanBoardInfos.set(sc.deviceInfo.id, sc);
      });
    });
  }

  /**
   * 加载布局
   * @private
   */
  private __loadLayout() {
    const sendBoxes = this.__allStatus.sendBoxs;
    for (const sb of sendBoxes) {
      const sbLayout = new SendBoxLayout(sb);
      if (this.__needAcc) sbLayout.accXY(this.screenLayout);
      this.sendBoxLayouts.add(sbLayout);
      this.__loadScanBoardsLayout(sb.layout.outPorts, sbLayout);
    }
  }

  /**
   * 加载扫描板布局
   * @param outPorts
   * @param sandBoxLayout
   * @private
   */
  private __loadScanBoardsLayout(outPorts: V3proSendBoxOutPort[], sandBoxLayout: SendBoxLayout) {
    for (const outPort of outPorts) {
      const outPortLayout = new OutPortLayout(outPort, sandBoxLayout);
      if (this.__needAcc) outPortLayout.accXY(sandBoxLayout);
      this.outPortLayouts.add(outPortLayout);
      // sandBoxLayout.addOutPort(outPortLayout);
      outPort.scanBoards.forEach((sc: any) => {
        if (this.scanBoardLayouts.hasByKey(sc.id)) return;
        const board = this.scanBoardInfos.get(sc.id)!;
        const scanBoardLayout = new ScanBoardLayout(board, sandBoxLayout, outPortLayout);
        if (this.__needAcc) scanBoardLayout.accXY(outPortLayout);
        // sandBoxLayout.addScanBoard(scanBoardLayout);
        // outPortLayout.addScanBoard(scanBoardLayout);
        this.scanBoardLayouts.add(scanBoardLayout);
        board?.status.panels.forEach((panel: any) => {
          const panelLayout = new PanelLayout(
            panel,
            scanBoardLayout,
            board,
            sandBoxLayout,
            outPortLayout,
            scanBoardLayout,
          );
          this.panelLayouts.add(panelLayout);
          // scanBoardLayout.addPanel(panelLayout);
        });
      });
    }
  }
}
